package onvifprotocol

import (
	"errors"
	"github.com/beevik/etree"
	"github.com/use-go/onvif/media"
	"github.com/use-go/onvif/xsd"
	onviftype "github.com/use-go/onvif/xsd/onvif"
	"go.uber.org/zap"
	"io/ioutil"
	"iot-base/common/logger"
	"net/http"
	"strconv"
)

func (m *CameraDevice) GetProfiles() ([]map[string]map[string]interface{}, error) {
	files := media.GetProfiles{}
	resp := make([]map[string]map[string]interface{}, 0, 1)
	result, err := m.CallMethod(files)
	if err != nil {
		return resp, err
	}
	doc := etree.NewDocument()
	err = doc.ReadFromBytes(result)
	if err != nil {
		return resp, err
	}
	fault := doc.FindElements("./Envelope/Body/Fault/Reason/Text")
	if len(fault) > 0 {
		return resp, errors.New(fault[0].Text())
	}
	root := doc.FindElement("./Envelope/Body/GetProfilesResponse")
	for _, profile := range root.SelectElements("Profiles") {
		data := make(map[string]map[string]interface{})
		data[profile.Tag] = make(map[string]interface{})
		data[profile.Tag]["Name"] = profile.SelectElement("Name").Text()
		for _, attr := range profile.Attr {
			data[profile.Tag][attr.Key] = attr.Value
		}
		// VideoSourceConfiguration
		videoSource := make(map[string]interface{})
		for _, videoSourceConfiguration := range profile.SelectElements("VideoSourceConfiguration") {
			for _, attr := range videoSourceConfiguration.Attr {
				videoSource[attr.Key] = attr.Value
			}
			for _, v := range videoSourceConfiguration.ChildElements() {
				switch v.Tag {
				case "Name", "UseCount":
					videoSource[v.Tag] = v.Text()
					break
				case "Bounds":
					Bounds := make(map[string]interface{})
					for _, attr := range v.Attr {
						Bounds[attr.Key] = attr.Value
					}
					videoSource[v.Tag] = Bounds
					break
					//case "Extension":
					//	Extension := make(map[string]map[string]interface{})
					//	Extension["Rotate"] = make(map[string]interface{})
					//	Extension["Rotate"]["Mode"] = v.SelectElement("Rotate").SelectElement("Mode").Text()
					//	videoSource[v.Tag] = Extension
				}
			}

		}

		// VideoEncoderConfiguration
		videoEncoder := make(map[string]interface{})
		for _, videoEncoderConfiguration := range profile.SelectElements("VideoEncoderConfiguration") {
			for _, attr := range videoEncoderConfiguration.Attr {
				videoEncoder[attr.Key] = attr.Value
			}
			for _, v := range videoEncoderConfiguration.ChildElements() {
				switch v.Tag {
				case "Name", "UseCount", "Encoding", "Quality", "SessionTimeout":
					videoEncoder[v.Tag] = v.Text()
					break
				case "Resolution", "RateControl", "H264":
					temp := make(map[string]interface{})
					for _, v1 := range v.ChildElements() {
						temp[v1.Tag] = v1.Text()
					}
					videoEncoder[v.Tag] = temp
				case "Multicast":
					temp := make(map[string]interface{})
					for _, v1 := range v.ChildElements() {
						switch v1.Tag {
						case "Port", "TTL", "AutoStart":
							temp[v1.Tag] = v1.Text()
							break
						case "Address":
							temp1 := make(map[string]interface{})
							for _, v2 := range v1.ChildElements() {
								temp1[v2.Tag] = v2.Text()
							}
							temp[v1.Tag] = temp1
						}
					}
					videoEncoder[v.Tag] = temp
				}
			}
		}
		data[profile.Tag]["VideoSourceConfiguration"] = videoSource
		data[profile.Tag]["VideoEncoderConfiguration"] = videoEncoder
		resp = append(resp, data)
	}
	return resp, nil
}

func readResponse(resp *http.Response) ([]byte, error) {
	b, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return []byte{}, err
	}
	return append([]byte{}, b...), nil
}

func (m *CameraDevice) SetProfile(data map[string]interface{}) (map[string]interface{}, error) {
	resp := make(map[string]interface{})
	videoEncoderConfiguration, err := m.GetVideoEncoderConfiguration(data["Token"].(string))
	if err != nil {
		return resp, err
	}
	videoEncoder := media.SetVideoEncoderConfiguration{}
	videoEncoder.Configuration = videoEncoderConfiguration
	videoEncoder.Configuration.Token = onviftype.ReferenceToken(data["Token"].(string))
	// Width Height 分辨率.
	videoEncoder.Configuration.Resolution.Width = xsd.Int(data["ResolutionWidth"].(float64))
	videoEncoder.Configuration.Resolution.Height = xsd.Int(data["ResolutionHeight"].(float64))
	// Quality 图像质量.
	videoEncoder.Configuration.Quality = data["Quality"].(float64)
	// Encoding 视频编码.
	videoEncoder.Configuration.Encoding = onviftype.VideoEncoding(data["Encoding"].(string))
	// GovLength I帧间隔.
	videoEncoder.Configuration.MPEG4.GovLength = xsd.Int(data["GovLength"].(float64))
	videoEncoder.Configuration.H264.GovLength = xsd.Int(data["GovLength"].(float64))
	videoEncoder.Configuration.H264.H264Profile = onviftype.H264Profile(data["Mpeg4Profile"].(string))
	// EncodingInterval 编码间隔.
	videoEncoder.Configuration.RateControl.EncodingInterval = xsd.Int(data["EncodingInterval"].(float64))
	// FrameRateLimit 帧速率限制.
	videoEncoder.Configuration.RateControl.FrameRateLimit = xsd.Int(data["FrameRateLimit"].(float64))
	// BitrateLimit  码率上限.
	videoEncoder.Configuration.RateControl.BitrateLimit = xsd.Int(data["BitrateLimit"].(float64))
	result, err := m.CallMethod(videoEncoder)
	if err != nil {
		return resp, err
	}
	doc := etree.NewDocument()
	err = doc.ReadFromBytes(result)
	if err != nil {
		return resp, err
	}
	fault := doc.FindElements("./Envelope/Body/Fault/Reason/Text")
	if len(fault) > 0 {
		return resp, errors.New(fault[0].Text())
	}
	return resp, nil
}

func (m *CameraDevice) GetCapabilities() {
}

func (m *CameraDevice) GetVideoEncoderConfiguration(configurationToken string) (onviftype.VideoEncoderConfiguration, error) {
	reqData := media.GetVideoEncoderConfiguration{
		ConfigurationToken: onviftype.ReferenceToken(configurationToken),
	}
	resp := onviftype.VideoEncoderConfiguration{}
	result, err := m.CallMethod(reqData)
	if err != nil {
		return resp, err
	}

	doc := etree.NewDocument()
	err = doc.ReadFromBytes(result)
	if err != nil {
		return resp, err
	}
	body := doc.FindElement("./Envelope/Body")
	fault := body.FindElements("./Fault/Reason/Text")
	if len(fault) > 0 {
		return resp, errors.New(fault[0].Text())
	}
	Configuration := body.FindElement("./GetVideoEncoderConfigurationResponse/Configuration")
	resp.ConfigurationEntity.Token = onviftype.ReferenceToken(Configuration.Attr[0].Value)
	resp.ConfigurationEntity.Name = onviftype.Name(Configuration.FindElement("Name").Text())
	UseCount, _ := strconv.Atoi(Configuration.FindElement("UseCount").Text())
	resp.ConfigurationEntity.UseCount = UseCount
	resp.Encoding = onviftype.VideoEncoding(Configuration.FindElement("Encoding").Text())
	// Resolution
	Width, _ := strconv.Atoi(Configuration.FindElement("./Resolution/Width").Text())
	resp.Resolution.Width = xsd.Int(Width)
	Height, _ := strconv.Atoi(Configuration.FindElement("./Resolution/Height").Text())
	resp.Resolution.Height = xsd.Int(Height)
	Quality, _ := strconv.ParseFloat(Configuration.FindElement("Quality").Text(), 64)
	resp.Quality = Quality
	// RateControl
	FrameRateLimit, _ := strconv.Atoi(Configuration.FindElement("./RateControl/FrameRateLimit").Text())
	resp.RateControl.FrameRateLimit = xsd.Int(FrameRateLimit)
	EncodingInterval, _ := strconv.Atoi(Configuration.FindElement("./RateControl/EncodingInterval").Text())
	resp.RateControl.EncodingInterval = xsd.Int(EncodingInterval)
	BitrateLimit, _ := strconv.Atoi(Configuration.FindElement("./RateControl/BitrateLimit").Text())
	resp.RateControl.BitrateLimit = xsd.Int(BitrateLimit)
	// H264
	GovLength, _ := strconv.Atoi(Configuration.FindElement("./H264/GovLength").Text())
	resp.H264.GovLength = xsd.Int(GovLength)
	resp.H264.H264Profile = onviftype.H264Profile(Configuration.FindElement("./H264/H264Profile").Text())
	// Multicast
	resp.Multicast.Address.Type = onviftype.IPType(Configuration.FindElement("./Multicast/Address/Type").Text())
	if resp.Multicast.Address.Type == "IPv4" {
		resp.Multicast.Address.IPv4Address = onviftype.IPv4Address(Configuration.FindElement("./Multicast/Address/IPv4Address").Text())
	} else {
		resp.Multicast.Address.IPv6Address = onviftype.IPv6Address(Configuration.FindElement("./Multicast/Address/IPv6Address").Text())
	}
	TTL, _ := strconv.Atoi(Configuration.FindElement("./Multicast/TTL").Text())
	resp.Multicast.TTL = TTL
	Port, _ := strconv.Atoi(Configuration.FindElement("./Multicast/TTL").Text())
	resp.Multicast.Port = Port
	AutoStart, _ := strconv.ParseBool(Configuration.FindElement("./Multicast/AutoStart").Text())
	resp.Multicast.AutoStart = xsd.Boolean(AutoStart)
	return resp, nil
}

func (m *CameraDevice) GetProfile(profileToken string) (map[string]interface{}, error) {
	resp := make(map[string]interface{})
	profile := media.GetProfile{
		ProfileToken: onviftype.ReferenceToken(profileToken),
	}
	result, err := m.CallMethod(profile)
	if err != nil {
		logger.Log.Error("RemovePreset device.CallMethod err:", zap.Error(err))
		return resp, err
	}
	doc := etree.NewDocument()
	err = doc.ReadFromBytes(result)
	if err != nil {
		return resp, err
	}
	fault := doc.FindElements("./Envelope/Body/Fault/Reason/Text")
	if len(fault) > 0 {
		return resp, errors.New(fault[0].Text())
	}
	root := doc.FindElements("./Envelope/Body/GetProfileResponse/Profile")
	return childElements(root[0].ChildElements()), nil
}

// ChildElements 解析子节点.
func childElements(elements []*etree.Element) map[string]interface{} {
	if len(elements) == 0 {
		return map[string]interface{}{}
	}
	resp := make(map[string]interface{})
	for _, v := range elements {
		if len(v.ChildElements()) == 0 {
			resp[v.Tag] = v.Text()
		} else {
			resp[v.Tag] = childElements(v.ChildElements())
		}
	}
	return resp
}
